// Copyright (c) MatrixFramework. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Matrix.ServiceCommunication.Options;
using Matrix.ServiceCommunication.Services;

namespace Matrix.ServiceCommunication.Authentication.Hybrid;

/// <summary>
/// 混合认证提供者
/// </summary>
public class HybridAuthenticationProvider : IAuthenticationProvider
{
    private readonly IOptionsMonitor<ServiceCommunicationOptions> _options;
    private readonly ILogger<HybridAuthenticationProvider> _logger;
    private readonly IAuthenticationProvider[] _authenticationProviders;
    private bool _disposed;

    public HybridAuthenticationProvider(
        IOptionsMonitor<ServiceCommunicationOptions> options,
        ILogger<HybridAuthenticationProvider> logger,
        ClientCredentialsAuthenticationProvider clientCredentialsProvider,
        JwtAuthenticationProvider jwtProvider,
        ApiKeyAuthenticationProvider apiKeyProvider)
    {
        _options = options ?? throw new ArgumentNullException(nameof(options));
        _logger = logger ?? throw new ArgumentNullException(nameof(logger));

        _authenticationProviders = new IAuthenticationProvider[]
        {
            clientCredentialsProvider ?? throw new ArgumentNullException(nameof(clientCredentialsProvider)),
            jwtProvider ?? throw new ArgumentNullException(nameof(jwtProvider)),
            apiKeyProvider ?? throw new ArgumentNullException(nameof(apiKeyProvider))
        };

        _logger.LogInformation("混合认证提供者已初始化，支持 {Count} 种认证方式", _authenticationProviders.Length);
    }

    public async Task<string> GetAccessTokenAsync(string serviceName, CancellationToken cancellationToken = default)
    {
        var options = _options.CurrentValue.Authentication;
        var authType = GetEffectiveAuthenticationType(serviceName, options);

        var provider = GetAuthenticationProvider(authType);
        if (provider == null)
        {
            _logger.LogWarning("未找到认证类型 {AuthenticationType} 的提供者", authType);
            return string.Empty;
        }

        try
        {
            _logger.LogDebug("使用认证类型 {AuthenticationType} 获取Token，服务: {ServiceName}", authType, serviceName);
            var token = await provider.GetAccessTokenAsync(serviceName, cancellationToken);

            if (!string.IsNullOrEmpty(token))
            {
                _logger.LogDebug("成功获取Token，服务: {ServiceName}, 认证类型: {AuthenticationType}",
                    serviceName, authType);
                return token;
            }
            else
            {
                _logger.LogWarning("获取Token失败，服务: {ServiceName}, 认证类型: {AuthenticationType}",
                    serviceName, authType);

                // 如果主要认证方式失败，尝试回退方案
                return await TryFallbackAuthenticationAsync(serviceName, authType, cancellationToken);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取Token异常，服务: {ServiceName}, 认证类型: {AuthenticationType}",
                serviceName, authType);

            // 尝试回退方案
            return await TryFallbackAuthenticationAsync(serviceName, authType, cancellationToken);
        }
    }

    public async Task<string> RefreshTokenAsync(string serviceName, CancellationToken cancellationToken = default)
    {
        var options = _options.CurrentValue.Authentication;
        var authType = GetEffectiveAuthenticationType(serviceName, options);

        var provider = GetAuthenticationProvider(authType);
        if (provider == null)
        {
            return string.Empty;
        }

        try
        {
            _logger.LogDebug("刷新Token，服务: {ServiceName}, 认证类型: {AuthenticationType}", serviceName, authType);
            var refreshedToken = await provider.RefreshTokenAsync(serviceName, cancellationToken);

            if (!string.IsNullOrEmpty(refreshedToken))
            {
                return refreshedToken;
            }

            // 如果刷新失败，尝试重新获取
            return await GetAccessTokenAsync(serviceName, cancellationToken);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "刷新Token异常，服务: {ServiceName}, 认证类型: {AuthenticationType}",
                serviceName, authType);

            // 刷新失败时尝试重新获取
            return await GetAccessTokenAsync(serviceName, cancellationToken);
        }
    }

    public async Task<bool> ValidateTokenAsync(string token, CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(token))
        {
            return false;
        }

        var options = _options.CurrentValue.Authentication;

        // 尝试使用所有可用的认证提供者来验证Token
        var validationTasks = _authenticationProviders.Select(provider =>
            ValidateTokenWithProvider(provider, token, cancellationToken));

        var validationResults = await Task.WhenAll(validationTasks);

        // 如果任何一个提供者验证成功，则认为Token有效
        var isValid = validationResults.Any(result => result);

        if (isValid)
        {
            _logger.LogDebug("Token验证成功");
        }
        else
        {
            _logger.LogWarning("Token验证失败，所有认证提供者都验证失败");
        }

        return isValid;
    }

    private AuthenticationType GetEffectiveAuthenticationType(string serviceName, AuthenticationOptions options)
    {
        // 如果指定了特定的认证类型，使用指定的类型
        if (options.Type != AuthenticationType.Hybrid)
        {
            return options.Type;
        }

        // 混合模式下，根据服务名称或配置选择认证类型
        // 这里可以实现复杂的策略，例如：
        // 1. 根据服务名称选择不同的认证类型
        // 2. 根据配置文件中的服务特定配置选择
        // 3. 根据环境变量或其他运行时条件选择

        // 默认优先级：JWT Bearer > API Key > ClientCredentials
        var jwtOptions = _options.CurrentValue.Authentication;
        if (!string.IsNullOrEmpty(jwtOptions.TokenEndpoint) &&
            !string.IsNullOrEmpty(jwtOptions.ClientId) &&
            !string.IsNullOrEmpty(jwtOptions.ClientSecret))
        {
            return AuthenticationType.JwtBearer;
        }

        if (!string.IsNullOrEmpty(jwtOptions.ClientSecret))
        {
            return AuthenticationType.ApiKey;
        }

        if (!string.IsNullOrEmpty(jwtOptions.TokenEndpoint))
        {
            return AuthenticationType.ClientCredentials;
        }

        return AuthenticationType.None;
    }

    private IAuthenticationProvider? GetAuthenticationProvider(AuthenticationType authType)
    {
        return authType switch
        {
            AuthenticationType.ClientCredentials => _authenticationProviders[0],
            AuthenticationType.JwtBearer => _authenticationProviders[1],
            AuthenticationType.ApiKey => _authenticationProviders[2],
            _ => null
        };
    }

    private async Task<string> TryFallbackAuthenticationAsync(
        string serviceName,
        AuthenticationType primaryAuthType,
        CancellationToken cancellationToken)
    {
        _logger.LogDebug("尝试回退认证方案，主认证类型: {PrimaryAuthType}, 服务: {ServiceName}",
            primaryAuthType, serviceName);

        // 定义回退优先级
        var fallbackOrder = new AuthenticationType[]
        {
            AuthenticationType.JwtBearer,
            AuthenticationType.ApiKey,
            AuthenticationType.ClientCredentials
        };

        foreach (var authType in fallbackOrder.Where(t => t != primaryAuthType))
        {
            var provider = GetAuthenticationProvider(authType);
            if (provider != null)
            {
                try
                {
                    var token = await provider.GetAccessTokenAsync(serviceName, cancellationToken);
                    if (!string.IsNullOrEmpty(token))
                    {
                        _logger.LogInformation("回退认证成功，使用认证类型: {AuthenticationType}, 服务: {ServiceName}",
                            authType, serviceName);
                        return token;
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogDebug(ex, "回退认证失败，认证类型: {AuthenticationType}, 服务: {ServiceName}",
                        authType, serviceName);
                }
            }
        }

        _logger.LogWarning("所有回退认证方案都失败了，服务: {ServiceName}", serviceName);
        return string.Empty;
    }

    private async Task<bool> ValidateTokenWithProvider(
        IAuthenticationProvider provider,
        string token,
        CancellationToken cancellationToken)
    {
        try
        {
            return await provider.ValidateTokenAsync(token, cancellationToken);
        }
        catch (Exception ex)
        {
            _logger.LogDebug(ex, "认证提供者验证Token失败: {ProviderType}", provider.GetType().Name);
            return false;
        }
    }

    public void Dispose()
    {
        if (_disposed) return;

        _disposed = true;

        foreach (var provider in _authenticationProviders)
        {
            if (provider is IDisposable disposableProvider)
            {
                disposableProvider.Dispose();
            }
        }

        _logger.LogDebug("混合认证提供者已释放");
    }
}